Linux 0.12
1. printf
linux-0.12/init/main.c,
static int printf(const char *fmt, ...)
{
    va_list args;
    int i;
    va_start(args, fmt);
    write(1,printbuf,i=vsprintf(printbuf, fmt, args));
    va_end(args);
    return i;
}
printf第一个参数const char *fmt,随后是可变参数。
1.1 va_start和va_end
linux-0.12/include/stdarg.h,
typedef char *va_list;
#ifndef __sparc__
#define va_start(AP, LASTARG)                         \
 (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#else
#define va_start(AP, LASTARG)                         \
 (__builtin_saveregs (),                        \
  AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG)))
#endif
__sparc__,sparc是可扩充处理器架构(Scalable Processor ARChitecture)的缩写,是一种精简指令集计算机指令集架构
(1)va_start
(char *) &(LASTARG),获取LASTARG的地址,并将类型转换为字符指针char *__va_rounded_size (LASTARG)是一个与平台相关的宏,用于计算LASTARG的大小,并向上舍入到对齐边界。如平台要求8字节对齐,LASTARG大小为5,那么__va_rounded_size (LASTARG)返回值为8+ __va_rounded_size(LASTARG),跳过LASTARG,使指针指向第一个可变参数
综上,AP(即args)被初始化为指向第一个可变参数的地址。
(2)va_end(args)
va_end(args),结束对可变参数列表的访问。以下是其展开?
#define va_end(ap) ((void)(ap = 0))
1.2 write
write在linux-0.12/include/unistd.h声明,
int write(int fildes, const char * buf, off_t count);
通过宏_syscall3,定义了write系统调用,
#define __LIBRARY__
#include <unistd.h>
_syscall3(int,write,int,fd,const char *,buf,off_t,count)
_syscall3在linux-0.12/include/unistd.h定义,
#define _syscall3(type,name,atype,a,btype,b,ctype,c) \
type name(atype a,btype b,ctype c) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
    : "=a" (__res) \
    : "0" (__NR_##name),"b" ((long)(a)),"c" ((long)(b)),"d" ((long)(c))); \
if (__res>=0) \
    return (type) __res; \
errno=-__res; \
return -1; \
}
__NR_##name,此时值为__NR_write,即为4,
#define __NR_write    4
int $0x80,执行系统调用_system_call:,其中call _sys_call_table(,%eax,4)实现程序跳转,跳转地址为_sys_call_table + %eax * 4,eax存放的是系统调用功能号,即跳转到函数sys_write。关于系统调用见:Linux 0.12系统调用。
1.3 sys_write
系统调用函数sys_write在linux-0.12/fs/read_write.c文件中定义,
int sys_write(unsigned int fd,char * buf,int count)
{
    struct file * file;
    struct m_inode * inode;
    if (fd>=NR_OPEN || count <0 || !(file=current->filp[fd]))
        return -EINVAL;
    if (!count)
        return 0;
    inode=file->f_inode;
    if (inode->i_pipe)
        return (file->f_mode&2)?write_pipe(inode,buf,count):-EIO;
    if (S_ISCHR(inode->i_mode))
        return rw_char(WRITE,inode->i_zone[0],buf,count,&file->f_pos);
    if (S_ISBLK(inode->i_mode))
        return block_write(inode->i_zone[0],&file->f_pos,buf,count);
    if (S_ISREG(inode->i_mode))
        return file_write(inode,file,buf,count);
    printk("(Write)inode->i_mode=%06o\n\r",inode->i_mode);
    return -EINVAL;
}
根据设备不同,调用不同的写入函数。
2. printk
printf并不能直接在内核中使用,因为内核代码中不能直接使用专用于用户模式的fs段寄存器(fs指向局部数据段),而内核态运行的程序要打印的信息在内核数据段中。
因此,内核需要实现自己的打印函数,即printk。printk在linux-0.12/kernel/printk.c文件中定义,
/*
 * When in kernel-mode, we cannot use printf, as fs is liable to
 * point to 'interesting' things. Make a printf with fs-saving, and
 * all is well.
 */
#include <stdarg.h>
#include <stddef.h>
#include <linux/kernel.h>
static char buf[1024];
extern int vsprintf(char * buf, const char * fmt, va_list args);
int printk(const char *fmt, ...)
{
    va_list args;
    int i;
    va_start(args, fmt);
    i=vsprintf(buf,fmt,args);
    va_end(args);
    console_print(buf);
    return i;
}
2.1 vsprintf
linux-0.12/kernel/vsprintf.c,
int vsprintf(char *buf, const char *fmt, va_list args)
{
    int len;
    int i;
    char * str;
    char *s;
    int *ip;
    int flags;        /* flags to number() */
    int field_width;    /* width of output field */
    int precision;        /* min. # of digits for integers; max
                   number of chars for from string */
    int qualifier;        /* 'h', 'l', or 'L' for integer fields */
    for (str=buf ; *fmt ; ++fmt) {
        if (*fmt != '%') {
            *str++ = *fmt;
            continue;
        }
        /* process flags */
        flags = 0;
        repeat:
            ++fmt;        /* this also skips first '%' */
            switch (*fmt) {
                case '-': flags |= LEFT; goto repeat;
                case '+': flags |= PLUS; goto repeat;
                case ' ': flags |= SPACE; goto repeat;
                case '#': flags |= SPECIAL; goto repeat;
                case '0': flags |= ZEROPAD; goto repeat;
                }
        /* get field width */
        field_width = -1;
        if (is_digit(*fmt))
            field_width = skip_atoi(&fmt);
        else if (*fmt == '*') {
            /* it's the next argument */
            field_width = va_arg(args, int);
            if (field_width < 0) {
                field_width = -field_width;
                flags |= LEFT;
            }
        }
        /* get the precision */
        precision = -1;
        if (*fmt == '.') {
            ++fmt;    
            if (is_digit(*fmt))
                precision = skip_atoi(&fmt);
            else if (*fmt == '*') {
                /* it's the next argument */
                precision = va_arg(args, int);
            }
            if (precision < 0)
                precision = 0;
        }
        /* get the conversion qualifier */
        qualifier = -1;
        if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
            qualifier = *fmt;
            ++fmt;
        }
        switch (*fmt) {
        case 'c':
            if (!(flags & LEFT))
                while (--field_width > 0)
                    *str++ = ' ';
            *str++ = (unsigned char) va_arg(args, int);
            while (--field_width > 0)
                *str++ = ' ';
            break;
        case 's':
            s = va_arg(args, char *);
            len = strlen(s);
            if (precision < 0)
                precision = len;
            else if (len > precision)
                len = precision;
            if (!(flags & LEFT))
                while (len < field_width--)
                    *str++ = ' ';
            for (i = 0; i < len; ++i)
                *str++ = *s++;
            while (len < field_width--)
                *str++ = ' ';
            break;
        case 'o':
            str = number(str, va_arg(args, unsigned long), 8,
                field_width, precision, flags);
            break;
        case 'p':
            if (field_width == -1) {
                field_width = 8;
                flags |= ZEROPAD;
            }
            str = number(str,
                (unsigned long) va_arg(args, void *), 16,
                field_width, precision, flags);
            break;
        case 'x':
            flags |= SMALL;
        case 'X':
            str = number(str, va_arg(args, unsigned long), 16,
                field_width, precision, flags);
            break;
        case 'd':
        case 'i':
            flags |= SIGN;
        case 'u':
            str = number(str, va_arg(args, unsigned long), 10,
                field_width, precision, flags);
            break;
        case 'n':
            ip = va_arg(args, int *);
            *ip = (str - buf);
            break;
        default:
            if (*fmt != '%')
                *str++ = '%';
            if (*fmt)
                *str++ = *fmt;
            else
                --fmt;
            break;
        }
    }
    *str = '\0';
    return str-buf;
}
2.2 console.c
linux-0.12/kernel/console.c,
void console_print(const char * b)
{
    int currcons = fg_console;
    char c;
    while (c = *(b++)) {
        if (c == 10) {
            cr(currcons);
            lf(currcons);
            continue;
        }
        if (c == 13) {
            cr(currcons);
            continue;
        }
        if (x>=video_num_columns) {
            x -= video_num_columns;
            pos -= video_size_row;
            lf(currcons);
        }
        __asm__("movb %2,%%ah\n\t"
            "movw %%ax,%1\n\t"
            ::"a" (c),
            "m" (*(short *)pos),
            "m" (attr)
            :"ax");
        pos += 2;
        x++;
    }
    set_cursor(currcons);
}